﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Microsoft.LiveFX.ResourceModel;
using Wintellect.LiveFx;

namespace Wintellect.LiveFx.Cloudboard
{
  /// <summary>
  /// Interaction logic for ClipboardRing.xaml
  /// </summary>
  public partial class ClipboardRing : Window
  {
    internal const int MaxClipboardItems = 50;
    public static RoutedCommand PasteSelectedItem = new RoutedCommand();
    public static RoutedCommand AddItemFromClipboard = new RoutedCommand();

    private Clipboard clipboard;
    private Storyboard openAnimation;
    private Storyboard fadeInAnimation;
    private IntPtr previousWindow;
    private IntPtr previousFocus;
    private Point lastMousePosition;

    public ClipboardRing(Clipboard clipboard)
    {
      InitializeComponent();

      this.clipboard = clipboard;
      this.Title = String.Format("{0} - Wintellect Cloudboard", this.clipboard.Name);

      this.clipboard.OfflineChanged += (o, e) =>
      {
        if (this.clipboard.Offline) this.Close();
      };

      this.MouseLeftButtonDown += (o,e) =>
      {
        if (e.Source == this)
        {
          // Find out where the mouse is (in screen coordinates)
          this.lastMousePosition = NativeMethods.GetMouseScreenPosition(null);
          this.CaptureMouse();
        }
      };
      this.MouseMove += (o, e) =>
      {
        Point currentMousePosition = NativeMethods.GetMouseScreenPosition(null);
        if (this.IsMouseCaptured && currentMousePosition != this.lastMousePosition)
        {
          // Calculate the new position, and ensure that the window is offset by the difference between the origin and the current position
          Vector vector = Point.Subtract(currentMousePosition, this.lastMousePosition);
          this.Top = this.Top + vector.Y;
          this.Left = this.Left + vector.X;
          this.lastMousePosition = currentMousePosition;
        }
      };
      this.MouseLeftButtonUp += (o, e) =>
      {
        if (this.IsMouseCaptured)
        {
          this.ReleaseMouseCapture();
        }
      };

      this.fadeInAnimation = this.Resources["FadeInAnimation"] as Storyboard;

      this.layout.Width = 0;
      this.layout.Height = 0;
      this.layout.HorizontalAlignment = HorizontalAlignment.Center;
      this.layout.VerticalAlignment = VerticalAlignment.Center;
      this.layout.Visibility = Visibility.Collapsed;

      // Necessary to capture keyboard navigation events
      this.Loaded += (o, e) => {
        this.Focus();
        this.Items.Focus();
        this.Dispatcher.BeginInvoke(new Action(() => {
          this.FetchItems();
          this.openAnimation = this.CreateOpenAnimation();
          this.openAnimation.Completed += delegate
          {
            this.Items.Focus();
            this.layout.HorizontalAlignment = HorizontalAlignment.Stretch;
            this.layout.VerticalAlignment = VerticalAlignment.Stretch;
          };
          this.openAnimation.Begin(this.layout); 
        }));
        this.ShowBusyIndicator();
      };

      clipboard.ItemsChanged += (o, e) =>
      {
        this.Dispatcher.BeginInvoke(new Action(() => { this.FetchItems(); }));
      };
    }

    private Storyboard CreateOpenAnimation()
    {
      Storyboard output = new Storyboard();
      output.FillBehavior = FillBehavior.HoldEnd;
      output.Duration = TimeSpan.FromMilliseconds(500);
      //<Storyboard x:Key="OpenAnimation" Completed="OpenAnimation_Completed" Storyboard.TargetName="layout">
      //  <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.5000000" Storyboard.TargetName="{x:Null}" Storyboard.TargetProperty="(FrameworkElement.Width)">
      //    <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
      //    <SplineDoubleKeyFrame KeyTime="00:00:00.4" Value="550"/>
      //    <SplineDoubleKeyFrame KeyTime="00:00:00.4999" Value="500"/>
      //    <SplineDoubleKeyFrame KeyTime="00:00:00.5" Value="NaN"/>
      //  </DoubleAnimationUsingKeyFrames>
      //  <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.5000000" Storyboard.TargetName="{x:Null}" Storyboard.TargetProperty="(FrameworkElement.Height)">
      //    <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
      //    <SplineDoubleKeyFrame KeyTime="00:00:00.4" Value="330"/>
      //    <SplineDoubleKeyFrame KeyTime="00:00:00.4999" Value="300"/>
      //    <SplineDoubleKeyFrame KeyTime="00:00:00.5" Value="NaN"/>
      //  </DoubleAnimationUsingKeyFrames>
      //  <ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="layout" Storyboard.TargetProperty="(UIElement.Visibility)">
      //    <DiscreteObjectKeyFrame KeyTime="00:00:00" Value="{x:Static Visibility.Collapsed}"/>
      //    <DiscreteObjectKeyFrame KeyTime="00:00:00.01" Value="{x:Static Visibility.Visible}"/>
      //  </ObjectAnimationUsingKeyFrames>
      //  <ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="layout" Storyboard.TargetProperty="(FrameworkElement.HorizontalAlignment)">
      //    <DiscreteObjectKeyFrame KeyTime="00:00:00.49" Value="{x:Static HorizontalAlignment.Stretch}"/>
      //  </ObjectAnimationUsingKeyFrames>
      //  <ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="layout" Storyboard.TargetProperty="(FrameworkElement.VerticalAlignment)">
      //    <DiscreteObjectKeyFrame KeyTime="00:00:00.49" Value="{x:Static VerticalAlignment.Stretch}"/>
      //  </ObjectAnimationUsingKeyFrames>
      //</Storyboard>
      DoubleAnimationUsingKeyFrames widthAnimation = new DoubleAnimationUsingKeyFrames()
      {
        Duration = TimeSpan.FromMilliseconds(500),
        KeyFrames = new DoubleKeyFrameCollection()
        {
          new SplineDoubleKeyFrame(0, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(0))),
          new SplineDoubleKeyFrame(1.1D * this.ActualWidth, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(450))),
          new SplineDoubleKeyFrame(this.ActualWidth, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(499))),
          new DiscreteDoubleKeyFrame(Double.NaN, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(500)))
        }
      };
      Storyboard.SetTargetProperty(widthAnimation, new PropertyPath(FrameworkElement.WidthProperty));
      output.Children.Add(widthAnimation);
      DoubleAnimationUsingKeyFrames heightAnimation = new DoubleAnimationUsingKeyFrames()
      {
        Duration = TimeSpan.FromMilliseconds(500),
        KeyFrames = new DoubleKeyFrameCollection()
        {
          new SplineDoubleKeyFrame(0, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(0))),
          new SplineDoubleKeyFrame(1.1D * this.ActualHeight, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(450))),
          new SplineDoubleKeyFrame(this.ActualHeight, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(499))),
          new DiscreteDoubleKeyFrame(Double.NaN, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(500)))
        }
      };
      Storyboard.SetTargetProperty(heightAnimation, new PropertyPath(FrameworkElement.HeightProperty));
      output.Children.Add(heightAnimation);
      ObjectAnimationUsingKeyFrames visibilityAnimation = new ObjectAnimationUsingKeyFrames()
      {
        Duration = TimeSpan.FromMilliseconds(500),
        KeyFrames = new ObjectKeyFrameCollection()
        {
          new DiscreteObjectKeyFrame(Visibility.Visible, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(1)))
        }
      };
      Storyboard.SetTargetProperty(visibilityAnimation, new PropertyPath(UIElement.VisibilityProperty));
      output.Children.Add(visibilityAnimation);

      return output;
    }

    private void ShowBusyIndicator()
    {
      this.Items.Items.Clear();
      this.busyIndicator.Visibility = Visibility.Visible;
    }

    private void HideBusyIndicator()
    {
      this.Items.Opacity = 0;
      this.Items.Visibility = Visibility.Visible;
      this.Items.Focus();

      this.busyIndicator.Visibility = Visibility.Collapsed;
      fadeInAnimation.Begin(this.Items);
    }

    private void FetchItems()
    {
      this.HideBusyIndicator();

      IList<ClipboardItem> items = clipboard.ClipboardItems;
      this.Items.Items.Clear();
      for (int index = 0; index < items.Count; index++)
      {
        ClipboardItem currentItem = items[index];
        this.Items.Items.Add(currentItem.Media);
      }
    }

    private void PrepareToShow(IntPtr previousWindow, IntPtr previousFocus)
    {
      this.previousWindow = previousWindow;
      this.previousFocus = previousFocus;
      this.Items.Items.Clear();
      this.Items.Visibility = Visibility.Collapsed;
      this.Visibility = Visibility.Visible;
      this.Focus();
    }

    public void Show(IntPtr previousWindow, IntPtr previousFocus)
    {
      this.PrepareToShow(previousWindow, previousFocus);
      this.Show();
    }

    public void ShowDialog(IntPtr previousWindow, IntPtr previousFocus)
    {
      this.PrepareToShow(previousWindow, previousFocus);
      this.ShowDialog();
    }

    private void OpenAnimation_Completed(object sender, EventArgs e)
    {
      this.Items.Focus();
    }

    private void CloseCommandExecuted(Object sender, ExecutedRoutedEventArgs e)
    {
      this.Close();
      e.Handled = true;
    }

    private void DeleteCommandExecuted(Object sender, ExecutedRoutedEventArgs e)
    {
      ClipboardItem item = clipboard.ClipboardItems[this.Items.SelectedIndex];
      clipboard.DeleteItem(item);

      e.Handled = true;
    }

    private void DeleteCommandCanExecute(Object sender, CanExecuteRoutedEventArgs e)
    {
      if (null != this.Items.SelectedItem) e.CanExecute = true;
    }

    private void CopyCommandExecuted(Object sender, ExecutedRoutedEventArgs e)
    {
      this.clipboard.CopyItemToSystemClipboard(this.Items.SelectedIndex);
      e.Handled = true;
    }

    private void CopyCommandCanExecute(Object sender, CanExecuteRoutedEventArgs e)
    {
      if (this.Items.Items.Count > 0 && null != this.Items.SelectedItem) e.CanExecute = true;
    }

    private void PasteCommandExecuted(Object sender, ExecutedRoutedEventArgs e)
    {
      this.clipboard.AddItemFromSystemClipboard();
      e.Handled = true;
    }

    private void AddItemCommandExecuted(Object sender, ExecutedRoutedEventArgs e)
    {
      this.clipboard.AddItemFromSystemClipboard();

      e.Handled = true;
    }

    private void AddItemCommandCanExecute(Object sender, CanExecuteRoutedEventArgs e)
    {
      if (
        System.Windows.Clipboard.ContainsImage() || 
        System.Windows.Clipboard.ContainsText())
      {
        e.CanExecute = true;
      }
    }

    private void PasteCurrentItemCommandExecuted(Object sender, ExecutedRoutedEventArgs e)
    {
      this.clipboard.CopyItemToSystemClipboard(this.Items.SelectedIndex);

      if (IntPtr.Zero != this.previousWindow && IntPtr.Zero != this.previousFocus)
      {
        NativeMethods.Paste(this.previousWindow, this.previousFocus);
      }

      this.Close();

      e.Handled = true;
    }

    private void PasteCurrentItemCommandCanExecute(Object sender, CanExecuteRoutedEventArgs e)
    {
      if (null != this.Items.SelectedItem) e.CanExecute = true;
    }

    private void ManagePermissions_Click(object sender, EventArgs e)
    {
      ManagePermissions foo = new ManagePermissions(this.clipboard);
      foo.Owner = this;
      foo.ShowDialog();
    }
  }
}
